package com.koron.operation.classes.shift.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.koron.bean.base.BaseBean;
import com.koron.bean.query.PageQuery;
import com.koron.bean.scheduleShift.ScheduleShift;
import com.koron.bean.scheduleShift.ShiftStaff;
import com.koron.bean.system.org.OrgVo;
import com.koron.bean.system.role.AppOrgJurisdictionBean;
import com.koron.bean.system.role.UserOwnJurisdictionBean;
import com.koron.bean.system.staff.po.AppUserPo;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.business.common.mapper.StaffMapper;
import com.koron.common.core.business.configsettings.utils.ConfigInfoTool;
import com.koron.common.core.business.print.ReportTask;
import com.koron.common.core.business.print.printstandard.PrintStandardMapper;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.message.MessageUtil;
import com.koron.common.task.web.entity.CronTask;
import com.koron.common.task.web.service.XxlJobService;
import com.koron.operation.classes.schedule.bean.dto.*;
import com.koron.operation.classes.schedule.bean.po.ClassesSchedule;
import com.koron.operation.classes.schedule.bean.po.ClassesShift;
import com.koron.operation.classes.schedule.bean.po.ClassesStaff;
import com.koron.operation.classes.schedule.bean.query.ScheduleQuery;
import com.koron.operation.classes.schedule.bean.query.ShiftStaffQuery;
import com.koron.operation.classes.schedule.bean.vo.ClassesStaffVO;
import com.koron.operation.classes.schedule.mapper.ClassesScheduleMapper;
import com.koron.operation.classes.schedule.service.ClassesScheduleService;
import com.koron.operation.classes.schedule.service.ClassesShiftService;
import com.koron.operation.classes.schedule.service.ClassesStaffService;
import com.koron.operation.classes.shift.bean.dto.*;
import com.koron.operation.classes.shift.bean.param.ClassesAdjustParam;
import com.koron.operation.classes.shift.bean.po.ClassesAdjustRecord;
import com.koron.operation.classes.shift.bean.po.ClassesDutyRecord;
import com.koron.operation.classes.shift.bean.po.ClassesTip;
import com.koron.operation.classes.shift.bean.po.ClassesWorkCompletion;
import com.koron.operation.classes.shift.bean.print.ShiftPrintDTO;
import com.koron.operation.classes.shift.bean.query.ShiftQuery;
import com.koron.operation.classes.shift.bean.query.ShiftRecordQuery;
import com.koron.operation.classes.shift.bean.query.WorkFeignQuery;
import com.koron.operation.classes.shift.bean.vo.ShiftRecordExportVO;
import com.koron.operation.classes.shift.bean.vo.ShiftRecordVO;
import com.koron.operation.classes.shift.feign.OrderFeignService;
import com.koron.operation.classes.shift.service.ClassesAdjustRecordService;
import com.koron.operation.classes.shift.service.ClassesDutyRecordService;
import com.koron.operation.classes.shift.service.ClassesTipService;
import com.koron.operation.classes.shift.service.ShiftService;
import com.koron.operation.util.DelayUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <pre>
 * 【交接班状态】
 * 未接班 0
 * 值班中 1
 * 已交班 2
 *
 * 【交接班业务规则】
 * 1、非本值人员不允许点击交班按钮
 * 2、非下值人员不允许点击接班按钮
 * 3、调整人员时，不允许调整为非本单位的人员，可以增加或减少人员但至少要留一个人值班
 * 4、交班时，本值人员必须全部签完名才能提交
 * 5、接班时，下值人员必须全部签完名才能提交
 * 6、若下值为空白班次，无需接班，本值交完班即可完成班次切换。
 * 7、若本值为空白班次，无需交班，下值接完班即可完成班次切换。
 * 8、业务单与班次的绑定规则：交班确认完成之前，页面上展示本值班次所产生的业务单。此时业务单并未和班次绑定。交班确认完成之后，业务单与班次绑定。
 * 9、未完成业务单的界定范围：业务单的计划完成时间在本值班次时间范围内，且值班人员为以下任意责任人之一（工作负责人、工作跟踪负责人、工作班成员、巡视人员），且状态为未完成。
 * 10、已完成业务单的界定范围：业务单的归档时间在本值班次时间范围内，且值班人员为以下任意责任人之一（工作负责人、工作跟踪负责人、工作班成员、巡视人员），且状态为已完成。
 * 11、交接事项的内容自动展示上一班次的，当交接事项配置发生改变后，已保存的交接事项不受影响，更新配置后可按最新的配置进行填写。
 * 12、下值人员接班时，若发现交接事项填写有误，可驳回给本值人员重新填写。
 *
 * 【最后一班为什么一直是值班中】
 * 最后一班没人能接上去，就一直是值班中
 * 没有下一个班次是不能交班的，否则交接班衔接不起来
 * 如果最后一班允许交班，如果有新的排班方案，下一班次将衔接不上去
 * 如果最后一班允许交班，如果有多个新的排班方案，将不知道哪个才是上一班次，这是一对多模型
 * 如果最后一班允许交班，如果有多个最后一班，如果有多个新的排班方案，排班将彻底混乱
 *
 * </pre>
 * @author jzp
 * @since 2021/10/27
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ShiftServiceImpl implements ShiftService {
    private final ClassesScheduleService classesScheduleService;
    private final ClassesShiftService classesShiftService;
    private final ClassesStaffService classesStaffService;
    private final ClassesAdjustRecordService classesAdjustRecordService;
    private final ClassesShiftRecordServiceImpl classesShiftRecordService;
    private final ClassesWorkCompletionServiceImpl workCompletionService;
    private final ClassesTipService tipService;
    private final OrderFeignService orderFeignService;
    private final ClassesDutyRecordService classesDutyRecordService;
    private final MessageUtil messageUtil;
    private final XxlJobService xxlJobService;

    private EamUser getCurrUser() {
        return ThreadLocalContext.get();
    }
    private String getCurrDs() {
        return ThreadLocalContext.get().getCurrDs();
    }

    private void setCurrDs(String mark) {
        ThreadLocalContext.get().setCurrDs(mark);
    }

    /**
     * 获取交接班面板信息
     */
    @Override
    @TaskAnnotation("getChangeShift")
    public ChangeShiftDTO getChangeShift(SessionFactory factory, ShiftRecordQuery query) {
        ChangeShiftDTO changeShiftDTO = new ChangeShiftDTO();

        // 1. 获取所有的排班方案
        ScheduleQuery scheduleQuery = BeanUtil.toBean(query, ScheduleQuery.class);
        scheduleQuery.setStatus(Collections.singletonList(0)); // 只查询启用的
        List<ClassesScheduleDTO> scheduleList = classesScheduleService.listNoStaffDto(factory, scheduleQuery);
        if (CollectionUtils.isEmpty(scheduleList)) {
            return changeShiftDTO;
        }

        Map<String, ClassesScheduleDTO> autoSwitchMap = scheduleList.stream().collect(Collectors.toMap(ClassesScheduleDTO::getId, e -> e, (i1, i2) -> i1));

        // 获取所有的排班班次
        List<ClassesShiftDTO> shifts = new ArrayList<>();
        scheduleList.forEach(e -> shifts.addAll(e.getShifts() == null ? new ArrayList<>() : e.getShifts()));
        if (CollectionUtils.isEmpty(shifts)) {
            return changeShiftDTO;
        }

        // 2. 根据开始时间进行倒序排序
        shifts.sort(Comparator.comparing(ClassesShiftDTO::getStartTime).reversed());

        // 3. 筛选值班中1的数据
        ClassesShiftDTO thisClassesShiftDTO = null;
        List<ClassesShiftDTO> collect = shifts.stream().filter(e -> 1 == e.getStatus()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(collect)) {
            // 如果有值班中1的数据，取之，找时间最前面的值班中的数据
            thisClassesShiftDTO = collect.get(collect.size() - 1);
        } else {
            // 如果没有，则取已交班2的数据
            List<ClassesShiftDTO> collect1 = shifts.stream().filter(e -> 2 == e.getStatus()).collect(Collectors.toList());
            // 获取最前面的未值班状态数据，找时间最后面的值班中的数据
            if (!CollectionUtils.isEmpty(collect1)) {
                thisClassesShiftDTO = collect1.get(0);
            }
        }
        // *
        changeShiftDTO.setThisClassesShift(thisClassesShiftDTO);
        if (thisClassesShiftDTO != null && thisClassesShiftDTO.getId() != null) {
            changeShiftDTO.setThisClassesShift(classesShiftService.getDtoById(factory, thisClassesShiftDTO.getId()));
        }
        // 4. 筛选出下一班次
        ClassesShiftDTO nextClassesShiftDTO = null;
        if (thisClassesShiftDTO == null) {
            // 4.1. 如果当前班次为空，则获取第一班数据，找时间最前面的值班中的数据
            nextClassesShiftDTO = shifts.get(shifts.size()-1);
        } else {
            // 4.2. 否则获取下一班次
            int i = shifts.indexOf(thisClassesShiftDTO);
            if (i - 1 >= 0) {
                // 当前班次后面时间的班次
                nextClassesShiftDTO = shifts.get(i - 1);
            }
        }
        // *
        changeShiftDTO.setNextClassesShift(nextClassesShiftDTO);
        if (nextClassesShiftDTO != null && nextClassesShiftDTO.getId() != null) {
            changeShiftDTO.setNextClassesShift(classesShiftService.getDtoById(factory, nextClassesShiftDTO.getId()));
        }

        // 5. 查询人员调整记录、是否自动交接班、
        if (thisClassesShiftDTO != null) {
            ClassesAdjustRecord classesAdjustRecord = new ClassesAdjustRecord();
            classesAdjustRecord.setShiftId(thisClassesShiftDTO.getId());
            List<ClassesAdjustRecord> list = classesAdjustRecordService.list(factory, classesAdjustRecord);
            // *
            changeShiftDTO.setAdjustRecords(list);
            ClassesScheduleDTO classesScheduleDTO = autoSwitchMap.get(thisClassesShiftDTO.getScheduleId());
            changeShiftDTO.setThisAutoSwitch(classesScheduleDTO.getAutoSwitch());
            changeShiftDTO.setThisScheduleId(classesScheduleDTO.getId());
            changeShiftDTO.setThisScheduleName(classesScheduleDTO.getName());
        }
        if (nextClassesShiftDTO != null) {
            ClassesScheduleDTO classesScheduleDTO = autoSwitchMap.get(nextClassesShiftDTO.getScheduleId());
            changeShiftDTO.setNextAutoSwitch(classesScheduleDTO.getAutoSwitch());
            changeShiftDTO.setNextScheduleId(classesScheduleDTO.getId());
            changeShiftDTO.setNextScheduleName(classesScheduleDTO.getName());
        }

        return changeShiftDTO;
    }

    /**
     * 交接班记录分页查询
     */
    @Override
    @TaskAnnotation("exportList")
    public List<ShiftRecordExportVO> exportList(SessionFactory factory, ShiftRecordQuery query) {
        List<ShiftRecordVO> list = classesShiftRecordService.listVo(factory, query);
        List<ShiftRecordExportVO> exportList = new ArrayList<>();
        for (ShiftRecordVO shiftRecordVO : list) {
            ShiftRecordExportVO shiftRecordExportVO = BeanUtil.toBean(shiftRecordVO, ShiftRecordExportVO.class);
            shiftRecordExportVO.setStartDate(shiftRecordVO.getThisStartTime());
            shiftRecordExportVO.setThisStaffStr(shiftRecordVO.getThisStaffs());
            shiftRecordExportVO.setNextStaffStr(shiftRecordVO.getNextStaffs());
            exportList.add(shiftRecordExportVO);
        }
        return exportList;
    }

    /**
     * 交接班记录分页查询
     */
    @Override
    @TaskAnnotation("page")
    public PageInfo<ShiftRecordVO> page(SessionFactory factory, PageQuery pageQuery, ShiftRecordQuery query) {
        // 设置权限
        query.setDeptIds(getCurrUserOrgIds());
        return classesShiftRecordService.page(factory, pageQuery, query);
    }

    //获取当前用户组织权限id(过滤出当前租户的组织)
    private List<String> getCurrUserOrgIds() {
        List<OrgVo> orgVoList = getCurrUser().getOrgVoList();
        UserOwnJurisdictionBean userOwnJurisdictionBean = getCurrUser().getUserOwnJurisdictionBean();
        if (Objects.isNull(userOwnJurisdictionBean)) {
            return new ArrayList<>();
        }

        List<AppOrgJurisdictionBean> appOrgJurisdictionBeanList = userOwnJurisdictionBean.getAppOrgJurisdictionBeanList();

        if (CollectionUtils.isEmpty(appOrgJurisdictionBeanList)) {
            return new ArrayList<>();
        }

        //当前水司为空情况下
        if (com.koron.common.core.util.StringUtils.isEmpty(getCurrDs())) {
            List<String> list = new ArrayList<>();
            appOrgJurisdictionBeanList.forEach(p->list.addAll(p.getOrgStrList()));
            return list.stream().distinct().collect(Collectors.toList());
        }
        for (AppOrgJurisdictionBean appOrgJurisdictionBean : appOrgJurisdictionBeanList) {

            if (appOrgJurisdictionBean.getMark().equals(getCurrDs())) {
                return appOrgJurisdictionBean.getOrgStrList();
            }
        }

        return new ArrayList<>();
    }

    /**
     * 交接班记录详情查询
     */
    @Override
    @TaskAnnotation("detail")
    public ShiftRecordDTO detail(SessionFactory factory, ShiftQuery query) {
        ShiftRecordDTO shiftRecordDTO = classesShiftRecordService.getByCondition(factory, query);
        // 设置提前交班时间戳
        Optional.ofNullable(shiftRecordDTO.getThisShift())
                .map(ClassesShift::getScheduleId)
                .map(e -> classesScheduleService.getById(factory, e))
                .ifPresent(e -> shiftRecordDTO.setAdvanceTime(e.computeAdvanceTime()));
        return shiftRecordDTO;
    }

    /**
     * 人员调整记录查询
     */
    @Override
    @TaskAnnotation("adjustRecord")
    public AdjustRecordDTO adjustRecord(SessionFactory factory, String thisShiftId, String nextShiftId) {
        AdjustRecordDTO adjustRecordDTO = new AdjustRecordDTO();

        // 查询交接班基本记录
        ClassesAdjustRecord classesAdjustRecord = new ClassesAdjustRecord();
        // 查询本值人员调整记录记录
        if (!StringUtils.isEmpty(thisShiftId)) {
            classesAdjustRecord.setShiftId(thisShiftId);
            adjustRecordDTO.setThisAdjustRecords(classesAdjustRecordService.list(factory, classesAdjustRecord));
        }

        // 查询下值人员调整记录记录
        if (!StringUtils.isEmpty(nextShiftId)) {
            classesAdjustRecord.setShiftId(nextShiftId);
            adjustRecordDTO.setNextAdjustRecords(classesAdjustRecordService.list(factory, classesAdjustRecord));
        }

        return adjustRecordDTO;
    }

    /**
     * 交接事项查询
     */
    @Override
    @TaskAnnotation("getTipByCondition")
    public List<ClassesTip> getTipByCondition(SessionFactory factory, String deptId, String locationId, String staffType) {
        WorkFeignQuery workFeignQuery = new WorkFeignQuery();
        workFeignQuery.setDeptId(deptId);
        workFeignQuery.setLocationId(locationId);
        workFeignQuery.setStaffType(staffType);
        return tipService.getFeignByCondition(workFeignQuery);
    }

    @Override
    @TaskAnnotation("staffs")
    public List<ClassesStaffVO> staffs(SessionFactory factory, String deptId, String locationId) {
        // 给每个账户都添加用户信息
        List<ClassesStaffVO> classesStaffVOList = new ArrayList<>();
        // 1. 查询当前部门和地点的所有排班方案
        ScheduleQuery query = new ScheduleQuery();
        query.setDeptId(deptId);
        query.setLocationId(locationId);
        query.setStatus(Collections.singletonList(0));
        List<ClassesScheduleDTO> classesScheduleDTOS = classesScheduleService.listNoStaffDto(factory, query);
        if (classesScheduleDTOS == null) {
            return classesStaffVOList;
        }
        // 优化，查询查询各个班次的人员
        List<String> classesShiftDTOIdsNoStaffs = classesScheduleDTOS.stream()
                .map(ClassesScheduleDTO::getShifts)
                .flatMap(Collection::stream)
                .filter(w -> w.getStatus() == 1) // 筛选值班中1的数据
                .map(BaseBean::getId)
                .collect(Collectors.toList());
        List<ClassesShiftDTO> classesShiftDTOList = classesShiftService.listDtoByIds(factory, classesShiftDTOIdsNoStaffs);
        if (classesShiftDTOList == null) {
            return classesStaffVOList;
        }
        List<ClassesStaff> collect = classesShiftDTOList.stream()
                .map(ClassesShiftDTO::getStaffs)
                .flatMap(Collection::stream)
                .distinct().sorted(Comparator.comparing(ClassesStaff::getType))
                .collect(Collectors.toList());
        // 账号集合
        List<String> ids = collect.stream().map(ClassesStaff::getStaffBy).collect(Collectors.toList());
        Map<String, AppUserPo> accountMap = new LinkedHashMap<>();
        if (!CollectionUtils.isEmpty(ids)) {
            factory.setDbEnv("_default");
            StaffMapper mapper = factory.getMapper(StaffMapper.class);
            List<AppUserPo> appUserPos = mapper.selectUserListByIds(ids);
            if (appUserPos != null) {
                accountMap = appUserPos.stream().collect(Collectors.toMap(AppUserPo::getAccount, e -> e, (o1,o2)->o1, LinkedHashMap::new));
            }
        }


        for (ClassesStaff classesStaff : collect) {
            ClassesStaffVO classesStaffVO = BeanUtil.toBean(classesStaff, ClassesStaffVO.class);
            classesStaffVO.setUser(accountMap.get(classesStaffVO.getStaffBy()));
            classesStaffVOList.add(classesStaffVO);
        }
        return classesStaffVOList;

    }

    /**
     * 查询首页值班信息
     * key : 部门名称（地点名称）
     */
    @Override
    @TaskAnnotation("home")
    public Map<String, List<ClassesStaffVO>> home(SessionFactory factory, String deptId) {
        Map<String, List<ClassesStaffVO>> result = new LinkedHashMap<>();

        // 1. 查询当前部门的所有排班方案
        ScheduleQuery query = new ScheduleQuery();
        query.setDeptId(deptId);
        query.setStatus(Collections.singletonList(0));
        List<ClassesScheduleDTO> classesScheduleDTOS = classesScheduleService.listNoStaffDto(factory, query);
        if (classesScheduleDTOS == null) {
            return result;
        }
        // 优化，查询查询各个班次的人员
        List<String> classesShiftDTOIdsNoStaffs = classesScheduleDTOS.stream()
                .map(ClassesScheduleDTO::getShifts)
                .flatMap(Collection::stream)
                .filter(w -> w.getStatus() == 1) // 筛选值班中1的数据
                .map(BaseBean::getId)
                .collect(Collectors.toList());
        List<ClassesShiftDTO> classesShiftDTOList = classesShiftService.listDtoByIds(factory, classesShiftDTOIdsNoStaffs);
        if (classesShiftDTOList == null) {
            return result;
        }
        Map<String, ClassesShiftDTO> classesShiftDTOMap = classesShiftDTOList.stream().collect(Collectors.toMap(BaseBean::getId, e -> e));
        Map<String, List<ClassesStaff>> map = new LinkedHashMap<>();
        // 根据部门名称（地点名称）对每个排班方案进行分组
        Map<String, List<ClassesScheduleDTO>> deptLocationNameMap = classesScheduleDTOS.stream()
                .collect(Collectors.groupingBy(e -> Objects.toString(e.getDeptName(), "") + (e.getLocationName() == null ? "" : "（" + e.getLocationName() + "）")));

        deptLocationNameMap.forEach((k, v) -> {
            List<ClassesStaff> collect = v.stream()
                    .map(ClassesScheduleDTO::getShifts)
                    .flatMap(Collection::stream)
                    .filter(w -> w.getStatus() == 1) // 筛选值班中1的数据
                    .map(e -> classesShiftDTOMap.get(e.getId()).getStaffs())
                    .flatMap(Collection::stream)
                    .distinct()
                    .sorted(Comparator.comparing(ClassesStaff::getType))
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                map.put(k, collect);
            }
        });

        // 2. 给值班人员补充用户信息
        // 2.1. 查询所有人员的用户信息
        List<String> ids = map.values().stream().flatMap(Collection::stream).map(ClassesStaff::getStaffBy).collect(Collectors.toList());
        Map<String, AppUserPo> accountMap = new LinkedHashMap<>();
        if (!CollectionUtils.isEmpty(ids)) {
            factory.setDbEnv("_default");
            StaffMapper mapper = factory.getMapper(StaffMapper.class);
            List<AppUserPo> appUserPos = mapper.selectUserListByIds(ids);
            if (appUserPos != null) {
                accountMap = appUserPos.stream().collect(Collectors.toMap(AppUserPo::getAccount, e -> e, (o1,o2)->o1, LinkedHashMap::new));
            }
        }
        // 2.2. 设置用户信息
        Map<String, AppUserPo> finalAccountMap1 = accountMap;
        map.forEach((k,v) -> {
            result.put(k, v.stream().map(classesStaff -> {
                ClassesStaffVO classesStaffVO = BeanUtil.toBean(classesStaff, ClassesStaffVO.class);
                classesStaffVO.setUser(finalAccountMap1.get(classesStaffVO.getStaffBy()));
                return classesStaffVO;
            }).collect(Collectors.toList()));
        });
        return result;
    }

    @Override
    @TaskAnnotation("leader")
    public List<Map<String, Object>> leader(SessionFactory factory) {
        // 1. 查询所有值班中的班次
        ClassesShift shiftQuery = new ClassesShift();
        shiftQuery.setStatus(1);
        List<ClassesShift> classesShiftList = classesShiftService.list(factory, shiftQuery);
        if (CollectionUtils.isEmpty(classesShiftList)) {
            return Collections.emptyList();
        }

        // 2. 查询所有OnCall的值班
        ScheduleQuery query = new ScheduleQuery();
        query.setStaffTypes(Collections.singletonList("3"));
        query.setStatus(Collections.singletonList(0));
        List<ClassesScheduleDTO> classesScheduleDTOList = classesScheduleService.listNoStaffDto(factory, query);
        if (CollectionUtils.isEmpty(classesScheduleDTOList)) {
            return Collections.emptyList();
        }
        // 1和2取交集 过滤出所有OnCall且值班中的班次
        // <scheduleId, deptName>
        Map<String, String> scheduleIdMap = classesScheduleDTOList.stream().collect(Collectors.toMap(ClassesScheduleDTO::getId, ClassesScheduleDTO::getDeptName, (o1,o2) -> o1, LinkedHashMap::new));
        List<String> onCallShiftIds = classesShiftList.stream().filter(e -> scheduleIdMap.containsKey(e.getScheduleId())).map(BaseBean::getId).collect(Collectors.toList());

        // 查询出所有的值班领导的人员
        List<ClassesStaff> collect = classesStaffService.listByShiftIdsAndTypes(factory, onCallShiftIds, Collections.singletonList("3"));

        // 账号集合
        List<String> ids = collect.stream().map(ClassesStaff::getStaffBy).collect(Collectors.toList());
        Map<String, AppUserPo> accountMap = new LinkedHashMap<>();
        if (!CollectionUtils.isEmpty(ids)) {
            factory.setDbEnv("_default");
            StaffMapper mapper = factory.getMapper(StaffMapper.class);
            List<AppUserPo> appUserPos = mapper.selectUserListByIds(ids);
            if (appUserPos != null) {
                accountMap = appUserPos.stream().collect(Collectors.toMap(AppUserPo::getAccount, e -> e, (o1,o2)->o1, LinkedHashMap::new));
            }
        }

        // 给每个账户都添加用户信息
        List<ClassesStaffVO> classesStaffVOList = new ArrayList<>();
        for (ClassesStaff classesStaff : collect) {
            ClassesStaffVO classesStaffVO = BeanUtil.toBean(classesStaff, ClassesStaffVO.class);
            classesStaffVO.setUser(accountMap.get(classesStaffVO.getStaffBy()));
            classesStaffVOList.add(classesStaffVO);
        }
        // <deptName, 人员list>
        Map<String, List<ClassesStaffVO>> deptStaffMap = classesStaffVOList.stream().collect(Collectors.groupingBy(e -> scheduleIdMap.get(e.getScheduleId())));
        List<Map<String, Object>> result = new ArrayList<>();
        for (String deptName : deptStaffMap.keySet()) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("deptName", deptName);
            map.put("staffs", deptStaffMap.get(deptName));
            result.add(map);
        }
        return result;
    }

    @Override
    @TaskAnnotation("staffShift")
    public StaffShiftInfo staffShift(SessionFactory factory, ShiftStaffQuery query) {
        StaffShiftInfo result = new StaffShiftInfo();
        // 1. 查询当前人员所有值班班次
        // 设置时间范围，[本月1号-7, 本月月底+7]
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(query.getDate());

        // 获取初始时间
        calendar.set(Calendar.DATE, 1);

        // 减7天
        Instant minus = calendar.toInstant().minus(7, ChronoUnit.DAYS);
        Date startTime = Date.from(minus);

        // 获取下个月第7天
        Instant plus = calendar.toInstant().plus(38, ChronoUnit.DAYS);
        Date endTime = Date.from(plus);

        query.setDates(Arrays.asList(startTime, endTime));
        List<ShiftStaffDTO> shiftStaffDTOS = classesShiftService.listDto(factory, query);

        // 2. 查询统计情况
        Map<String, Object> dayGroup = new LinkedHashMap<>();
        result.setDayGroup(dayGroup);
        // 查询时间范围内是否要值班 true为已值 1值班中 2已交班; false为未值 0未接班
        Map<String, Boolean> startMap = shiftStaffDTOS.stream().collect(Collectors.toMap(e -> DateUtil.format(e.getStartTime(), "yyyy-MM-dd"), e -> Arrays.asList(1, 2).contains(e.getStatus()), (o1, o2) -> o1 && o2, LinkedHashMap::new));
        Map<String, Boolean> endMap = shiftStaffDTOS.stream().collect(Collectors.toMap(e -> DateUtil.format(e.getEndTime(), "yyyy-MM-dd"), e -> Arrays.asList(1, 2).contains(e.getStatus()), (o1, o2) -> o1 && o2, LinkedHashMap::new));
        // 将endMap合并到startMap
        endMap.forEach((k,v) -> {
            // 只要有未值，则全天未值
            startMap.compute(k, (key, oldV) -> oldV == null ? v : v && oldV);
        });

        // 每天的值班情况
        List<Map<String, String>> markDays = new ArrayList<>();
        dayGroup.put("markDays",markDays);
        startMap.forEach((k,v) -> {
            Map<String, String> map = new HashMap<>();
            map.put("date", k);
            map.put("done", v.toString());
            markDays.add(map);
        });

        // 清除掉非本月的
        String targetMonth = DateFormatUtils.format(query.getDate(), "yyyy-MM");
        startMap.entrySet().removeIf(e -> !e.getKey().substring(0, 7).equals(targetMonth));
        // 统计信息 false未值needDays, true已值completeDays
        Map<Boolean, Long> completeDaysMap = startMap.values().stream().collect(Collectors.partitioningBy(e -> e, Collectors.counting()));
        dayGroup.put("completeDays", completeDaysMap.get(true));
        dayGroup.put("needDays", completeDaysMap.get(false));

        // 3. 获取指定天数所有值班
        String targetDate = DateFormatUtils.format(query.getDate(), "yyyy-MM-dd");
        List<String> shiftIds = shiftStaffDTOS.stream().filter(e -> {
            String sTime = DateFormatUtils.format(e.getStartTime(), "yyyy-MM-dd");
            String eTime = DateFormatUtils.format(e.getEndTime(), "yyyy-MM-dd");
            return StrUtil.equalsAny(targetDate, sTime, eTime);
        }).map(BaseBean::getId).collect(Collectors.toList());
        // 查询当前班次的信息
        List<ClassesShiftScheduleDTO> classesShiftDTOS = classesShiftService.listShiftScheduleDTOByIds(factory, shiftIds);
        if (CollectionUtils.isEmpty(classesShiftDTOS)) {
            return result;
        }
        /*
         * 获取上下班次：
         * 3.1. 拿到所有的ScheduleId
         * 3.2. 获取到这些ScheduleId对应的shift
         * 3.3. 根据 单位+地点+类型 获取对应的ScheduleId
         * 3.4. 根据 单位+地点+类型 获取对应的shift
         * 3.5. 拿到当前shift的上下shift
         */
        // 3.1.拿到所有的ScheduleId
        List<String> allSchduleIds = classesShiftDTOS.stream().map(ClassesShiftScheduleDTO::getClassesSchedule).map(BaseBean::getId).distinct().collect(Collectors.toList());
        ScheduleQuery scheduleQuery = new ScheduleQuery();
        scheduleQuery.setIds(allSchduleIds);
        // 3.2. 获取到这些ScheduleId对应的shift
        List<ClassesScheduleDTO> classesScheduleDTOList = classesScheduleService.listNoStaffDto(factory, scheduleQuery);
        Map<String, Integer> autoSwitchMap = classesScheduleDTOList.stream().collect(Collectors.toMap(BaseBean::getId, ClassesSchedule::getAutoSwitch));
        Map<String, List<ClassesShiftDTO>> scheduleMap = classesScheduleDTOList.stream().collect(Collectors.toMap(BaseBean::getId, ClassesScheduleDTO::getShifts));
        // 3.3. 根据 单位+地点+类型 获取对应的ScheduleId
        Map<String, List<ClassesSchedule>> deptLocationTypeMap = classesShiftDTOS.stream().map(ClassesShiftScheduleDTO::getClassesSchedule)
                .collect(Collectors.groupingBy(classesSchedule -> {
                    String deptId = classesSchedule.getDeptId();
                    String locationId = classesSchedule.getLocationId();
                    String staffType = classesSchedule.getStaffType();
                    return String.format("%s::%s::%s", deptId, locationId, staffType);
                }));
        // 3.4. 根据 单位+地点+类型 获取对应的shift
        Map<String, List<ClassesShiftDTO>> deptLocationTypeShiftMap = new HashMap<>();
        deptLocationTypeMap.forEach((k,v) -> {
            List<ClassesShiftDTO> value = v.stream()
                    .map(BaseBean::getId)
                    .distinct()
                    .map(scheduleMap::get)
                    .flatMap(Collection::stream)
                    .sorted(Comparator.comparing(ClassesShift::getStartTime))
                    .collect(Collectors.toList());
            deptLocationTypeShiftMap.put(k, value);
        });
        // 3.5. 拿到当前shift的上下shift
        classesShiftDTOS.forEach(classesShiftScheduleDTO -> {
            // 3.5.1. 获取当前shift的单位+地点+类型
            ClassesSchedule classesSchedule = classesShiftScheduleDTO.getClassesSchedule();
            String deptId = classesSchedule.getDeptId();
            String locationId = classesSchedule.getLocationId();
            String staffType = classesSchedule.getStaffType();
            String key = String.format("%s::%s::%s", deptId, locationId, staffType);
            // 3.5.2. 获取对应的shift
            List<ClassesShiftDTO> value = deptLocationTypeShiftMap.get(key);
            // 3.5.3. 获取上下shift
            for (int i = 0; i < value.size(); i++) {
                if (Objects.equals(value.get(i).getId(), classesShiftScheduleDTO.getId())) {
                    // 设置上值
                    if (i > 0) {
                        ClassesShiftDTO preClassesShiftDTO = value.get(i - 1);
                        classesShiftScheduleDTO.setPreClassesShiftDTO(preClassesShiftDTO);
                        classesShiftScheduleDTO.setPreAutoSwitch(autoSwitchMap.get(preClassesShiftDTO.getScheduleId()));
                    }
                    // 设置下值
                    if (i < value.size() - 1) {
                        ClassesShiftDTO nextClassesShiftDTO = value.get(i + 1);
                        classesShiftScheduleDTO.setNextClassesShiftDTO(nextClassesShiftDTO);
                        classesShiftScheduleDTO.setNextAutoSwitch(autoSwitchMap.get(nextClassesShiftDTO.getScheduleId()));
                    }
                    // 处理极端情况 i=0或i=value.size()-1时。有可能没有上下值，但也有可能是在另一个排班方案中（这种情况要处理，即直接从交接班面板查询）。
                    if (i == 0 || i == value.size() - 1) {
                        ShiftRecordQuery recordQuery = new ShiftRecordQuery();
                        recordQuery.setDeptId(deptId);
                        recordQuery.setLocationId(locationId);
                        recordQuery.setStaffTypes(Collections.singletonList(staffType));
                        ChangeShiftDTO changeShift = getChangeShift(factory, recordQuery);
                        if (i == 0 && changeShift != null && changeShift.getThisClassesShift() != null && !Objects.equals(changeShift.getThisClassesShift().getId(), classesShiftScheduleDTO.getId())) {
                            classesShiftScheduleDTO.setPreClassesShiftDTO(changeShift.getThisClassesShift());
                            classesShiftScheduleDTO.setPreAutoSwitch(changeShift.getThisAutoSwitch());
                        }
                        if (i == value.size() - 1 && changeShift != null && changeShift.getNextClassesShift() != null && !Objects.equals(changeShift.getNextClassesShift().getId(), classesShiftScheduleDTO.getId())) {
                            classesShiftScheduleDTO.setNextClassesShiftDTO(changeShift.getNextClassesShift());
                            classesShiftScheduleDTO.setNextAutoSwitch(changeShift.getNextAutoSwitch());
                        }
                    }
                    break;
                }
            }
        });
        // 根据开始时间排序
        classesShiftDTOS.sort(Comparator.comparing(ClassesShift::getStartTime));
        result.setShifts(classesShiftDTOS);
        return result;
    }

    /**
     * 人员调整
     */
    @Override
    @TaskAnnotation("adjustStaff")
    public void adjustStaff(SessionFactory factory, ClassesAdjustParam classesAdjustParam) {
        Assert.hasText(classesAdjustParam.getShiftId(), "班次id不能为空");
        // 1. 查询班次信息，并设置调整前人眼信息
        ClassesShiftDTO classesShiftDTO = classesShiftService.getDtoById(factory, classesAdjustParam.getShiftId());
        classesAdjustParam.setBeforeStaffs(classesShiftDTO.getStaffs());

        // 2. 更新人员信息
        // 2.1. 删除所有人员信息数据
        classesStaffService.removeByShiftId(factory, classesShiftDTO.getId());
        // 2.2. 设置shiftId、scheduleId
        List<ClassesStaff> afterStaffs = classesAdjustParam.getAfterStaffs();
        afterStaffs.forEach(e -> {
            e.setScheduleId(classesShiftDTO.getScheduleId());
            e.setShiftId(classesShiftDTO.getId());
        });
        classesStaffService.saveBatch(factory, afterStaffs);

        // 3. 保存人员调整记录
        ClassesAdjustRecord classesAdjustRecord = BeanUtil.toBean(classesAdjustParam, ClassesAdjustRecord.class);
        classesAdjustRecord.setBeforeStaff(classesAdjustParam.toStaffStr(classesAdjustParam.getBeforeStaffs()));
        classesAdjustRecord.setAfterStaff(classesAdjustParam.toStaffStr(classesAdjustParam.getAfterStaffs()));
        classesAdjustRecordService.save(factory, classesAdjustRecord);

        // 4. 告诉巡视工单有人员变更
        invokePatchOrder(factory, classesShiftDTO.getId());
    }

    /**
     * 交接班记录保存或更新
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public boolean saveOrUpdate(SessionFactory factory, ShiftRecordDTO shiftRecordDTO) {
        // 不保存工作完成信息
        shiftRecordDTO.setWorkCompletions(null);
        if (shiftRecordDTO.getStatus() == null) {
            shiftRecordDTO.setStatus(0);
        }
        return classesShiftRecordService.saveOrUpdateDto(factory, shiftRecordDTO);
    }

    /**
     * 交班
     */
    @Override
    @TaskAnnotation("handover")
    public boolean handover(SessionFactory factory, ShiftRecordDTO shiftRecordDTO) {
        // 交班时下值必须是未接班0
        String nextShiftId = shiftRecordDTO.getNextShiftId();
        ClassesShift sourceNextShift = classesShiftService.getById(factory, nextShiftId);
        Assert.notNull(sourceNextShift, "下值班次已删除，请关闭页面，重新交班");
        Assert.state(Objects.equals(sourceNextShift.getStatus(), 0), "班次状态有修改，请关闭页面，重新交班。原因：下值已接班");
        // 修改当前班次信息为2
        classesShiftService.updateState(factory, shiftRecordDTO.getThisShiftId(), 2);
        // 设置本值实际交班时间
        shiftRecordDTO.setThisTime(new Date());
        // 设置工作内容
        List<ClassesWorkCompletion> workCompletions = workCompletionService.getFeignByCondition(shiftRecordDTO.extractWorkFeignQuery());
        shiftRecordDTO.setWorkCompletions(workCompletions);

        // 如果下值是空白班，下值直接接班
        ClassesShiftDTO nextShift = shiftRecordDTO.getNextShift();
        if (nextShift.getBlanked() == 1) {
            // 设置接班时间
            shiftRecordDTO.setNextTime(new Date());
            // 显示交接班记录
            shiftRecordDTO.setStatus(1);
            // 设置空白班的状态为1值班中
            classesShiftService.updateState(factory, nextShift.getId(), 1);
            Map<String, String> config = ConfigInfoTool.getConfigByCode("operation", "operation_shift");
            String advanceTopicTakeoverTime = config.get("advance_topic_takeover_time");
            // 如果没有此配置，就不会创建定时任务
            if (advanceTopicTakeoverTime != null) {
                long min = Integer.parseInt(advanceTopicTakeoverTime);
                long l = nextShift.getEndTime().getTime() + min * 60 * 1000;
                // 接班通知的业务id以T开头
                DelayUtils.putTask("T"+shiftRecordDTO.getScheduleId(), l,getCurrDs(), "topicTakeoverShift");
            }
        } else {
            // 默认交班不显示交接班记录
            shiftRecordDTO.setStatus(0);
            // 通知人员去接班
            // 触发时机：上一班交班时，通知下一班的值班人员去接班
            topicTakeoverShift(shiftRecordDTO);
        }

        // 交班时产生值班记录
        // 获取本值接班时间 = 上一个交接班的实际接班时间
        ShiftQuery shiftQuery = new ShiftQuery();
        shiftQuery.setNextShiftId(shiftRecordDTO.getThisShiftId());
        shiftQuery.setStatus(1);
        ShiftRecordDTO detail = classesShiftRecordService.getDTOOne(factory, shiftQuery);
        classesDutyRecordService.saveBatch(factory, genDuty(detail == null ? shiftRecordDTO.getThisShift().getStartTime() : detail.getNextTime(), shiftRecordDTO));

        return classesShiftRecordService.saveOrUpdateDto(factory, shiftRecordDTO);
    }

    private List<ClassesDutyRecord> genDuty(Date nextTime, ShiftRecordDTO shiftRecordDTO) {
        return Optional.ofNullable(shiftRecordDTO)
                .map(ShiftRecordDTO::getThisShift)
                .map(ClassesShiftDTO::getStaffs)
                .orElse(new ArrayList<>())
                .stream()
                .map(staff -> {
                    ClassesDutyRecord classesDutyRecord = new ClassesDutyRecord();
                    classesDutyRecord.build(staff.getStaffBy(),
                            staff.getStaffName(),
                            shiftRecordDTO.getDeptId(),
                            shiftRecordDTO.getDeptName(),
                            shiftRecordDTO.getLocationId(),
                            shiftRecordDTO.getLocationName(),
                            shiftRecordDTO.getThisShiftId(),
                            shiftRecordDTO.getThisShift().getName(),
                            staff.getType(),
                            nextTime,
                            nextTime,
                            shiftRecordDTO.getThisTime()
                    );
                    return classesDutyRecord;
                }).collect(Collectors.toList());
    }

    /**
     * 通知人员去接班
     * 触发时机：上一班交班时，通知下一班的值班人员去接班
     * 如果是空白班，则通过定时任务去触发
     * 接班通知的业务id以T开头
     */
    @XxlJob(value = "topicTakeoverShift")
    public void topicTakeoverShift(ShiftRecordDTO shiftRecordDTO){
        if (shiftRecordDTO == null) {
            CronTask cronTask = xxlJobService.getCronTaskByXxlJobId((int) XxlJobHelper.getJobId());
            String businessId = cronTask.getBusinessId();
            String mark = cronTask.getMark();
            if (mark != null) {
                setCurrDs(mark);
            }
            String scheduleId = StrUtil.strip(businessId, "T");
            // 定时任务
            shiftRecordDTO = getCurrShiftRecordDTO(scheduleId, getCurrDs());
        }
        if (shiftRecordDTO.getNextShift() != null && CollectionUtils.isNotEmpty(shiftRecordDTO.getNextShift().getStaffs())) {
            String staffByStr = shiftRecordDTO.getNextShift().getStaffs().stream().map(ClassesStaff::getStaffBy).collect(Collectors.joining(","));
            String staffNameStr = shiftRecordDTO.getNextShift().getStaffs().stream().map(ClassesStaff::getStaffName).collect(Collectors.joining(","));
            // 您好，请您及时交班。 值班单位：%s。值班地点：%s。 排班方案：%s。 值班日期：%s。 值班人员：%s。
            messageUtil.sendMessageWithTemplate("takeover_shift", null, staffByStr,
                    shiftRecordDTO.getDeptName(),
                    shiftRecordDTO.getLocationName(),
                    shiftRecordDTO.getScheduleName(),
                    DateUtil.format(shiftRecordDTO.getNextShift().getStartTime(), "yyyy-MM-dd HH:mm")
                            + "至"
                            + DateUtil.format(shiftRecordDTO.getNextShift().getEndTime(), "yyyy-MM-dd HH:mm"),
                    staffNameStr);
        }
    }


    /**
     * 通知人员去交班
     * 触发时机：本值接班时，创建定时任务，在交班时间通知值班人员去交班
     * 接班通知的业务id以H开头
     */
    @XxlJob(value = "topicHandoverShift")
    public void topicHandoverShift(){
        CronTask cronTask = xxlJobService.getCronTaskByXxlJobId((int) XxlJobHelper.getJobId());
        String businessId = cronTask.getBusinessId();
        String mark = cronTask.getMark();
        if (mark != null) {
            setCurrDs(mark);
        }
        String scheduleId = StrUtil.strip(businessId, "H");
        // 定时任务
        ShiftRecordDTO shiftRecordDTO = getCurrShiftRecordDTO(scheduleId, getCurrDs());
        if (shiftRecordDTO.getThisShift() != null && CollectionUtils.isNotEmpty(shiftRecordDTO.getThisShift().getStaffs())) {
            String staffByStr = shiftRecordDTO.getThisShift().getStaffs().stream().map(ClassesStaff::getStaffBy).collect(Collectors.joining(","));
            String staffNameStr = shiftRecordDTO.getThisShift().getStaffs().stream().map(ClassesStaff::getStaffName).collect(Collectors.joining(","));
            // 您好，请您及时接班。 值班单位：%s。值班地点：%s。 排班方案：%s。 值班日期：%s。 值班人员：%s。
            messageUtil.sendMessageWithTemplate("handover_shift", null, staffByStr,
                    shiftRecordDTO.getDeptName(),
                    shiftRecordDTO.getLocationName(),
                    shiftRecordDTO.getScheduleName(),
                    DateUtil.format(shiftRecordDTO.getThisShift().getStartTime(), "yyyy-MM-dd HH:mm")
                            + "至"
                            + DateUtil.format(shiftRecordDTO.getThisShift().getEndTime(), "yyyy-MM-dd HH:mm"),
                    staffNameStr);
        }
    }

    /**
     * 接班
     */
    @Override
    @TaskAnnotation("takeover")
    public boolean takeover(SessionFactory factory, String recordId) {
        // 查询本值，本值必须是已接班才允许接班
        ShiftQuery shiftQuery = new ShiftQuery();
        shiftQuery.setRecordId(recordId);
        // 查询改班次对应的交接班记录
        ShiftRecordDTO source = classesShiftRecordService.getDTOOne(factory, shiftQuery);
        Optional.ofNullable(source).map(ShiftRecordDTO::getThisShift).map(ClassesShift::getStatus).ifPresent(e -> {
            Assert.state(Objects.equals(e, 2), "班次状态有修改，请关闭页面，重新接班。原因：本值还未交班，可能是被驳回过");
            // 2. 修改值班记录中实际接班时间
            source.setNextTime(new Date());
            // 设置状态为显示
            source.setStatus(1);

            // 3. 设置状态为1，保存
            classesShiftService.updateState(factory, source.getNextShiftId(), 1);

            // 调用巡视工单，指定人员
            invokePatchOrder(factory, source.getNextShiftId());
            // 清空签名信息
            classesStaffService.clearSign(factory, source.getNextShiftId());
            Map<String, String> config = ConfigInfoTool.getConfigByCode("operation", "operation_shift");
            String advanceTopicHandoverTime = config.get("advance_topic_handover_time");
            // 如果没有此配置，就不会创建定时任务
            if (advanceTopicHandoverTime != null) {
                long min = Integer.parseInt(advanceTopicHandoverTime);
                long l = source.getNextShift().getEndTime().getTime() + min * 60 * 1000;
                if (min == 0) {
                    ClassesSchedule classesSchedule = classesScheduleService.getById(factory, source.getScheduleId());
                    l = source.getNextShift().getEndTime().getTime() + classesSchedule.computeAdvanceTime();
                }
                // 交班通知的业务id以H开头
                DelayUtils.putTask("H"+source.getScheduleId(), l, getCurrDs(), "topicHandoverShift");
            }
        });
        // 更新交接班记录
        classesShiftRecordService.update(factory, source);
        return true;
    }

    /**
     * 直接接班
     * 即空白班交班，下一班接班
     */
    @Override
    @TaskAnnotation("straightTakeover")
    public boolean straightTakeover(SessionFactory factory, String thisShiftId, String nextShiftId) {
        Map<String, String> config = ConfigInfoTool.getConfigByCode("operation", "operation_shift");
        String advanceTopicHandoverTime = config.get("advance_topic_handover_time");
        // 如果没有此配置，就不会创建定时任务
        if (advanceTopicHandoverTime != null) {
            ClassesShift classesShift = classesShiftService.getById(factory, nextShiftId);
            long min = Integer.parseInt(advanceTopicHandoverTime);
            long l = classesShift.getEndTime().getTime() + min * 60 * 1000;
            if (min == 0) {
                ClassesSchedule classesSchedule = classesScheduleService.getById(factory, classesShift.getScheduleId());
                l = classesShift.getEndTime().getTime() + classesSchedule.computeAdvanceTime();
            }
            // 交班通知的业务id以H开头
            DelayUtils.putTask("H"+classesShift.getScheduleId(), l, getCurrDs(), "topicHandoverShift");
        }

        // 如果没有上一班次，直接接班
        if (StringUtils.isEmpty(thisShiftId)) {
            invokePatchOrder(factory, nextShiftId);
            return classesShiftService.updateState(factory, nextShiftId, 1);
        }
        // 空白班直接接班
        ShiftQuery shiftQuery = new ShiftQuery();
        shiftQuery.setThisShiftId(thisShiftId);
        shiftQuery.setNextShiftId(nextShiftId);
        ShiftRecordDTO shiftRecordDTO = classesShiftRecordService.getByCondition(factory, shiftQuery);
        // 交班
        handover(factory, shiftRecordDTO);
        // 接班
        takeover(factory, shiftRecordDTO.getId());
        return true;
    }

    /**
     * 调用巡视工单，指定人员
     */
    private void invokePatchOrder(SessionFactory factory, String shiftId) {
        ClassesShiftDTO shiftDTO = classesShiftService.getDtoById(factory, shiftId);
        Assert.notNull(shiftDTO, "下值班次已删除，请驳回数据，重新交班");
        ClassesSchedule schedule = classesScheduleService.getById(factory, shiftDTO.getScheduleId());
        ScheduleShift scheduleShift = BeanUtil.toBean(schedule, ScheduleShift.class);
        scheduleShift.setStartTime(shiftDTO.getStartTime());
        scheduleShift.setEndTime(shiftDTO.getEndTime());
        scheduleShift.setStaffs(shiftDTO.getStaffs().stream().map(staff -> BeanUtil.toBean(staff, ShiftStaff.class)).collect(Collectors.toList()));
        orderFeignService.updatePatrolStaff(scheduleShift);
        log.info("调用巡视工单，指定人员。参数：{}", JSONObject.toJSONString(scheduleShift));
    }

    /**
     * 接班驳回
     */
    @Override
    @TaskAnnotation("reject")
    public boolean reject(SessionFactory factory, String recordId) {
        // 1. 查询改班次对应的交接班
        ShiftQuery shiftQuery = new ShiftQuery();
        shiftQuery.setRecordId(recordId);
        ShiftRecordDTO record = classesShiftRecordService.getDTOOne(factory, shiftQuery);

        // 驳回时，下值必须是未接班
        if (record.getNextShift() != null) {
            Assert.state(record.getNextShift().getStatus() == 0, "班次状态有修改，请关闭页面，重新交接班。原因：下值已接班");
        }

        // 2. 设置本值状态为1
        // 清空本值签名信息
        classesStaffService.clearSign(factory, record.getThisShiftId());
        classesShiftService.updateState(factory, record.getThisShiftId(), 1);

        // 通知人员去交班

        return true;
    }

    @Override
    @TaskAnnotation("printShift")
    public String printShift(SessionFactory factory, ShiftQuery query) {
        ShiftRecordDTO detail = detail(factory, query);
        String printTemplateId = factory.getMapper(PrintStandardMapper.class)
                .getTemplateListByBusinessCode("defaultShift")
                .stream()
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("没有该业务的打印模板"))
                .getId();

        ShiftPrintDTO shiftPrintDTO = Optional.ofNullable(detail).map(this::headData).orElseThrow(() -> new IllegalArgumentException("未找到交接班记录数据"));
        List<ShiftPrintDTO> single = Collections.singletonList(shiftPrintDTO);
        List<ShiftPrintDTO> collect = Optional.ofNullable(detail.getTips())
                .map(tips -> tips.size() == 0 ? single : tips.stream().map(tip -> {
                    ShiftPrintDTO cloner = shiftPrintDTO.clone();
                    cloner.setFirstLevel(tip.getFirstLevel());
                    cloner.setSecondLevel(tip.getSecondLevel());
                    cloner.setContent(tip.getContent());
                    return cloner;
                }).collect(Collectors.toList())
                ).orElse(single);
        return ReportTask.printData(ThreadLocalContext.get(), printTemplateId, collect);//存储打印数据，以及返回打印地址
    }

    private ShiftPrintDTO headData(ShiftRecordDTO detail) {
        ShiftPrintDTO shiftPrintDTO = new ShiftPrintDTO();
        Optional.ofNullable(detail.getThisShift()).ifPresent(t -> {
            shiftPrintDTO.setThisShiftName(t.getName());
            String thisShiftUserName = Optional.ofNullable(t.getStaffs())
                    .orElse(new ArrayList<>())
                    .stream()
                    .map(ClassesStaff::getStaffName)
                    .collect(Collectors.joining(" "));
            shiftPrintDTO.setThisShiftUserName(thisShiftUserName);
        });
        shiftPrintDTO.setNextTime(DateUtil.format(detail.getNextTime(), "yyyy-MM-dd HH:mm"));
        String nextShiftUserName = Optional.ofNullable(detail.getNextShift())
                .map(ClassesShiftDTO::getStaffs)
                .orElse(new ArrayList<>())
                .stream()
                .map(ClassesStaff::getStaffName)
                .collect(Collectors.joining(" "));
        shiftPrintDTO.setNextShiftUserName(nextShiftUserName);
        return shiftPrintDTO;
    }


    private ShiftRecordDTO getCurrShiftRecordDTO(String scheduleId, String mark) {
        ScheduleDTO scheduleDTO = ADOConnection.runTask(mark, classesScheduleService, "getDtoById", ScheduleDTO.class, scheduleId);
        // 查询交接班面板信息
        ShiftRecordQuery query = new ShiftRecordQuery();
        query.setDeptId(scheduleDTO.getDeptId());
        query.setLocationId(scheduleDTO.getLocationId());
        query.setStaffTypes(Collections.singletonList(scheduleDTO.getStaffType()));
        ChangeShiftDTO changeShiftDTO = ADOConnection.runTask(mark, this, "getChangeShift", ChangeShiftDTO.class, query);
        // 获取交班记录
        ShiftQuery shiftQuery = new ShiftQuery();

        // 设置本值id
        Optional.ofNullable(changeShiftDTO)
                .map(ChangeShiftDTO::getThisClassesShift)
                .map(BaseBean::getId)
                .ifPresent(shiftQuery::setThisShiftId);

        // 设置下值id
        Optional.ofNullable(changeShiftDTO)
                .map(ChangeShiftDTO::getNextClassesShift)
                .map(BaseBean::getId)
                .ifPresent(shiftQuery::setNextShiftId);

        // 如果本值为空，构造一个有下值信息的交接班记录数据
        if (shiftQuery.getThisShiftId() == null) {
            ShiftRecordDTO shiftRecordDTO = new ShiftRecordDTO();
            shiftRecordDTO.setScheduleId(scheduleDTO.getId());
            shiftRecordDTO.setScheduleName(scheduleDTO.getName());
            ClassesShiftDTO nextShiftDto = ADOConnection.runTask(mark, classesShiftService, "getDtoById", ClassesShiftDTO.class, shiftQuery.getNextShiftId());
            shiftRecordDTO.setNextShift(nextShiftDto);
            return shiftRecordDTO;
        }
        return ADOConnection.runTask(mark, this, "detail", ShiftRecordDTO.class, shiftQuery);
    }
    @Override
    @TaskAnnotation("getMonthClass")
    public List<MonthClassDTO> getMonthClass(SessionFactory factory, ScheduleQuery scheduleQuery){
        ClassesScheduleMapper mapper = factory.getMapper(ClassesScheduleMapper.class);
        List<ShiftStaffDTO> monthClass = mapper.getMonthClass(scheduleQuery);
        Map<String, List<ShiftStaffDTO>> collect = monthClass.stream().collect(Collectors.groupingBy(shiftStaffDTO -> shiftStaffDTO.getStaffName()));
        List<MonthClassDTO> result = new ArrayList<>();
        Calendar cal = Calendar.getInstance();
        for(String key:collect.keySet()){
            Map<Integer, List<ShiftStaffDTO>> collect1 = collect.get(key).stream().collect(Collectors.groupingBy(shiftStaffDTO -> {
                cal.setTime(shiftStaffDTO.getStartTime());
                return cal.get(Calendar.DAY_OF_MONTH);
            }));
            MonthClassDTO monthClassDTO = new MonthClassDTO();
            List<DayClass> classes = new ArrayList<>();
            String staffBy = null;
            String staffName = null;
            for (Integer key1:collect1.keySet()){
                DayClass dayClass = new DayClass();
                List<ShiftStaffDTO> dayList = new ArrayList<>();
                dayClass.setDay_of_month(key1);
                dayClass.setWeek(cal.get(Calendar.DAY_OF_WEEK) - 1);
                collect1.get(key1).forEach(shiftStaffDTO -> {
                    dayList.add(shiftStaffDTO);
                });
                dayClass.setDayList(dayList);
                if (CollectionUtils.isNotEmpty(dayList)){
                    staffBy=dayClass.getDayList().get(0).getStaffBy();
                    staffName=dayClass.getDayList().get(0).getStaffName();
                }
                classes.add(dayClass);
            }
            monthClassDTO.setClasses(classes);
            monthClassDTO.setStaff_by(staffBy);
            monthClassDTO.setName(staffName);
            result.add(monthClassDTO);
        }
        return result;

    }


}
